﻿global using Devonline.AspNetCore;
global using Devonline.Core;
global using Devonline.Entity;
global using static Devonline.Core.AppSettings;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Net.Http.Headers;
using OpenIddict.Client;
using OpenIddict.Client.WebIntegration;

namespace Devonline.Identity;

public static class ServiceExtensions
{
    /// <summary>
    /// 添加默认的授权服务
    /// </summary>
    /// <param name="services"></param>
    /// <param name="httpSetting"></param>
    /// <returns></returns>
    public static IServiceCollection AddDefaultAuthorization(this IServiceCollection services, HttpSetting httpSetting)
    {
        //Func<IServiceProvider, AuthorizationHandler> func = serviceProvider =>
        //{
        //    var logger = serviceProvider.GetRequiredService<ILogger<AuthorizationHandler>>();
        //    var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
        //    var cache = serviceProvider.GetRequiredService<IDistributedCache>();
        //    var context = serviceProvider.GetRequiredService<IdentityDbContext>();
        //    return new AuthorizationHandler(logger, httpContextAccessor, cache, context, appSetting);
        //};
        //services.AddTransient<IAuthorizationHandler, AuthorizationHandler>((Func<IServiceProvider, AuthorizationHandler>)((IServiceProvider serviceProvider) =>
        //{
        //    var logger = serviceProvider.GetRequiredService<ILogger<AuthorizationHandler>>();
        //    var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
        //    var cache = serviceProvider.GetRequiredService<IDistributedCache>();
        //    var context = serviceProvider.GetRequiredService<IdentityDbContext>();
        //    return new AuthorizationHandler(logger, httpContextAccessor, cache, context, appSetting);
        //}));

        //services.AddTransient<IAuthorizationHandler, AuthorizationHandler>();
        //services.AddTransient<IAuthorizationRequirement, AuthorizationRequirement>();

        services.AddDefaultAuthorization<AuthorizationHandler, AuthorizationRequirement>(httpSetting.AuthType == AuthType.Cookie ? CookieAuthenticationDefaults.AuthenticationScheme : JwtBearerDefaults.AuthenticationScheme);
        return services;
    }

    /// <summary>
    /// 添加 OpenIdConnect 认证客户端
    /// </summary>
    /// <param name="services">依赖注入容器服务</param>
    /// <param name="clients">客户端配置列表</param>
    /// <returns></returns>
    public static IServiceCollection AddOpenIdConnects(this IServiceCollection services, params OpenIddictClientRegistration[] clients)
    {
        services.AddOpenIddict()
        .AddClient(options =>
        {
            // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint
            // URI per provider, unless all the registered providers support returning a special "iss"
            // parameter containing their URL as part of authorization responses. For more information,
            // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4.
            var builder = options.UseWebProviders();
            var client = clients.FirstOrDefault(x => x.ProviderType == nameof(OpenIddictClientWebIntegrationConstants.GitHub));
            if (client is not null)
            {
                builder = builder.AddGitHub(options =>
                {
                    options.SetClientId(client.ClientId!)
                        .SetClientSecret(client.ClientSecret!)
                        .SetRedirectUri(client.RedirectUri!)
                        .SetProviderDisplayName(client.ProviderDisplayName!);
                });
            }

            client = clients.FirstOrDefault(x => x.ProviderType == nameof(OpenIddictClientWebIntegrationConstants.Gitee));
            if (client is not null)
            {
                builder = builder.AddGitee(options =>
                {
                    options.SetClientId(client.ClientId!)
                        .SetClientSecret(client.ClientSecret!)
                        .SetRedirectUri(client.RedirectUri!)
                        .SetProviderDisplayName(client.ProviderDisplayName!);
                });
            }
        });

        return services;
    }

    /// <summary>
    /// 将认证相关默认设置放入依赖注入容器
    /// </summary>
    /// <param name="services"></param>
    /// <returns></returns>
    public static IServiceCollection AddDefaultIdentity(this IServiceCollection services, HttpSetting httpSetting)
    {
        services.AddIdentity<User, Role>(options =>
        {
            // Password settings.
            options.Password.RequireDigit = true;
            options.Password.RequireLowercase = true;
            options.Password.RequireNonAlphanumeric = true;
            options.Password.RequireUppercase = true;
            options.Password.RequiredLength = 8;
            options.Password.RequiredUniqueChars = 1;

            // Lockout settings.
            options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromDays(1);
            options.Lockout.MaxFailedAccessAttempts = 5;
            options.Lockout.AllowedForNewUsers = true;

            // User settings.
            options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
            options.User.RequireUniqueEmail = false;

            // Identity Store.
            options.Stores.MaxLengthForKeys = 36;
            //options.Stores.ProtectPersonalData = true;
        })
        .AddEntityFrameworkStores<IdentityDbContext>()
        .AddErrorDescriber<DefaultIdentityErrorDescriber>();

        services.TryAddScoped<UserStore>();
        services.TryAddScoped<RoleStore>();
        services.TryAddScoped<GroupStore>();
        services.TryAddScoped<ResourceAccessStore>();
        services.TryAddScoped<DefaultIdentityErrorDescriber>();
        services.TryAddScoped<IDataService<UserClaim, int>, DataService<UserClaim, int>>();
        return services;
    }

    /// <summary>
    /// 注册默认的基于字符串主键的实体数据对象模型数据操作相关服务
    /// </summary>
    /// <param name="services"></param>
    /// <returns></returns>
    public static IServiceCollection AddIdentityDataService(this IServiceCollection services)
    {
        services.AddScoped<IAttachmentService, AttachmentService>();
        services.AddScoped(typeof(IDataService<>), typeof(DataService<>));
        services.AddScoped(typeof(IDataWithAttachmentService<>), typeof(DataWithAttachmentService<>));
        services.AddScoped(typeof(IDataWithCollectionService<,>), typeof(DataWithCollectionService<,>));
        services.AddScoped(typeof(IDataWithCollectionAndAttachmentService<,>), typeof(DataWithCollectionAndAttachmentService<,>));
        return services;
    }

    /// <summary>
    /// 注册默认服务
    /// </summary>
    /// <param name="services">依赖注入容器服务</param>
    /// <param name="httpSetting">HTTP 配置项</param>
    /// <returns></returns>
    public static IServiceCollection AddDefaultIdentityServices(this IServiceCollection services, HttpSetting httpSetting)
    {
        services.AddDefaultServices(httpSetting);
        services.AddScoped<AuthorizationService>();

        if (httpSetting.Identity is not null)
        {
            services.AddHttpClient<IdentityService>(nameof(IdentityService), httpClient =>
            {
                //if (httpSetting.Identity.Authority.ToUpperInvariant().StartsWith(nameof(ProtocolType.Https).ToUpperInvariant()))
                //{
                //    ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback((sender, cert, chain, errors) => true);
                //}

                httpClient.BaseAddress = new Uri(httpSetting.Identity.Authority);
                httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, ContentType.Json);
                httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, httpSetting.Identity.ClientId);
            });

            services.AddScoped<IdentityService>();
        }

        return services;
    }
}